#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2020 crane <crane@crane-pc>
#
# Distributed under terms of the MIT license.

"""
    定义/definitions:
        RoleBlock/角色类型: 竖着的五虎, 横着的五虎, 曹操, 小卒 四类
"""

from pprint import pprint


class RoleBlock:
    VerticalRole = 1
    HorizonRole = 2
    CaoCao = 3
    Soilder = 4
    Empty = 5
    Invalid = 6     # 越界的, 不存在的

    type_size_map =  {
        VerticalRole: (2, 1),       # 宽和高
        HorizonRole:  (1, 2),
        CaoCao:       (2, 2),
        Soilder:      (1, 1),
        Empty:        (1, 1),
        Invalid:      (0, 0),
    }

    def __init__(self, role_type, pos, name, huarong_board):
        self.role_type = role_type
        self.name = name

        self.height, self.width = self.type_size_map[role_type]
        self.row, self.col = pos

        if huarong_board:
            self.fill_huarong_board(huarong_board)

    @property
    def pos(self):
        return self.row, self.col

    def fill_huarong_board(self, huarong_board):
        for row in range(self.row, self.row + self.height):
            for col in range(self.col, self.col + self.width):
                huarong_board.set_pos_block(row, col, self)


    def try_move(self, move_direction, empty_state):
        need_empty_state = [1, 1]

        # 返回: is_success, self.new_pos, emptys_new_pos
        if abs(move_direction[0]) == 1:
            # 上下移动
            assert move_direction[1] == 0
            need_empty_state[1] = self.col
        elif abs(move_direction[1]) == 1:
            # 左右移动
            assert move_direction[0] == 0
            need_empty_state[0] = self.row
        else:
            assert False

        # 根据 self.pos + move_direction + empty_state 判断是否可以移动

        # 根据 self.pos + move_direction: 计算移动后的空块坐标 + 自己的坐标
        pass

    def is_at(self, row, col):
        return self.row == row and self.col == col

    def empty_relative(self, e2):
        if self.role_type != self.Empty or e2.role_type != self.Empty:
            raise Exception(str(self) + "is not Empty or " + str(e2) + "is not Empty type")

        # 如果空闲连着, 返回连接类型和空闲左上坐标

        if self.row == e2.row and abs(self.col - e2.col) == 1:
            # horizon
            height_width = (1, 2)
            if self.col < e2.col:
                return height_width, self
            else:
                return height_width, e2

        if self.col == e2.col and abs(self.row - e2.row) == 1:
            # vertial 空闲连接
            height_width = (2, 1)
            if self.row < e2.row:
                return height_width, self
            else:
                return height_width, e2

        # 没有连接: 无所有左上角空闲, 只用返回连接类型
        return (1, 1), self

    def around(self, huarong_board):
        assert self.role_type == self.Empty
        up = self.row -1, self.col
        down = self.row +1, self.col
        left = self.row, self.col-1
        right = self.row, self.col+1

        ret = []
        for pos in (up, down, left, right):
            row, col = pos
            if row < 0 or col < 0:
                ret.append(InvalidBlock)
            elif row >= huarong_board.rows or col >= huarong_board.cols:
                ret.append(InvalidBlock)
            else:
                ret.append( huarong_board.chess_board[row][col] )

        return ret

    def __str__(self):
        return self.name + ", "

    def combine(self, nei):
        if self.is_not_real_role():
            return [nei]
        elif nei.is_not_real_role():
            return [self]
        elif self is nei:
            return [self]
        else:
            return [self, nei]

        # assert nei is self or nei.is_not_real_role()
        # return nei

    def is_not_real_role(self):
        return self.role_type == self.Invalid or self.role_type == self.Empty

    # def go_up_down(self):
    #     pass

    # def go_left_up(self):
    #     pass

InvalidBlock = RoleBlock(RoleBlock.Invalid, (-10, -10), "Invalid", None)



class HuaRongBoard:
    Unfilled = 0
    Filled   = 1

    def __init__(self):
        self.rows = 5
        self.cols = 4

        self.chess_board = self.make_chess_board()   # 每个坐标中存放着人物block对象索引
        self.init_roles()

        self.states = set()
        self.show()

        self.run()

    def run(self):
        ret = self.recursive_try()

    def recursive_try(self):
        if self.caocao.is_at(4, 1):
            return True

        if not self.add_now_state():
            return False

        # 获取e1, e2四周角色block(注意剔除Empty)
        e1_around_blocks = self.empty1.around(self)
        e2_around_blocks = self.empty2.around(self)

        print("around1")
        for b in e1_around_blocks:
            print(b, end='')
        print("\naround2")
        for b in e2_around_blocks:
            print(b, end='')

        # 判断empty1 和 empty2相对位置
        empty_state, empty_top_left_block = self.empty1.empty_relative(self.empty2)
        print("\nrelative:", empty_state, empty_top_left_block)

        # 1, 分开
        if empty_state == (1, 1):
            self.loop_four_move(e1_around_blocks, empty_state)
            self.loop_four_move(e2_around_blocks, empty_state)
            return

        # 2, 垂直连接
        # 3, 垂直连接
        combine_four_blocks = []
        for i in range(4):
            e1_nei = e1_around_blocks[i]
            e2_nei = e2_around_blocks[i]
            neis = e1_nei.combine(e2_nei)
            print('neis ', str(neis))
            combine_four_blocks.append(neis)

        self.loop_four_move(combine_four_blocks, empty_state)

    def loop_four_move(self, four_wall_blocks, empty_state):
        # 不要传入empty_state, 传入两个empty的坐标
        four_move_directions = [ (1,0), (-1,0), (0, -1), (0, 1) ]

        e1_origin_pos = self.empty1.pos
        e2_origin_pos = self.empty2.pos

        for i in range(4):
            blocks = four_wall_blocks[i]
            move_direction = four_move_directions[i]

            for block in blocks:
                block_origin_pos = block.pos

                # TODO
                is_ok, block_new_pos, new_empty_poss = block.try_move(move_direction, empty_state)

    def add_now_state(self):
        l = []
        for block in self.blocks:
            # l.append((block.row, block.col))
            l.append(block.pos)

        state = tuple( sorted(l) )
        if state in self.states:
            return False
        else:
            self.states.add(state)
            return True

    def init_roles(self):
        self.vertical1 = RoleBlock(RoleBlock.VerticalRole, (0, 0), "黄忠", self)  # 黄忠
        self.vertical2 = RoleBlock(RoleBlock.VerticalRole, (2, 0), "张飞", self)  # 张飞
        self.vertical3 = RoleBlock(RoleBlock.VerticalRole, (0, 3), "赵云", self)  # 赵云
        self.vertical4 = RoleBlock(RoleBlock.VerticalRole, (2, 3), "马超", self)  # 马超

        self.caocao    = RoleBlock(RoleBlock.CaoCao,       (0, 1), "曹操", self)  # 曹操
        self.horizon   = RoleBlock(RoleBlock.HorizonRole,  (2, 1), "关羽", self)  # 关羽


        self.soilder1  = RoleBlock(RoleBlock.Soilder,      (3, 1), "卒一", self)  # 兵1
        self.soilder2  = RoleBlock(RoleBlock.Soilder,      (3, 2), "卒二", self)
        self.soilder3  = RoleBlock(RoleBlock.Soilder,      (4, 0), "卒三", self)
        self.soilder4  = RoleBlock(RoleBlock.Soilder,      (4, 3), "卒四", self)

        self.empty1    = RoleBlock(RoleBlock.Empty,        (4, 1), "空一", self)  # 空1
        self.empty2    = RoleBlock(RoleBlock.Empty,        (4, 2), "空二", self)

        self.blocks = [self.vertical1, self.vertical2, self.vertical3, self.vertical4, self.caocao, self.horizon, self.soilder1, self.soilder2, self.soilder3, self.soilder4]

    def show(self):
        for row in range(self.rows):
            for col in range(self.cols):
                print(self.chess_board[row][col], end='')

            print()

    def make_chess_board(self):
        one_row = [None] * self.cols
        matrix = [list(one_row) for i in range(self.rows)]

        return matrix

    def set_pos_block(self, row, col, role_block):
        self.chess_board[row][col] = role_block


def test():
    s = HuaRongBoard()

def main():
    print("start main")
    test()

if __name__ == "__main__":
    main()
